﻿#include "Images.h"

#include "Render/VulkanContext.h"
#include "Render/VulkanRecycleObjects.h"

CVulkanImage::~CVulkanImage()
{
    if(Image)
    {
        CVulkanContext::Get()->GetRecycleObjects()->Images.Add(Image);
        CVulkanContext::Get()->GetRecycleObjects()->MemoryBlocks.Add(MemoryBlock);
    }
}

void CVulkanImage::SetTexture2DImageParams(
    uint32_t InWidth
    , uint32_t InHeight
    , vk::Format InFormat
    )
{

    vk::ImageCreateInfo ImageCreateInfo;
    ImageCreateInfo.imageType = vk::ImageType::e2D;
    ImageCreateInfo.extent.width = InWidth;
    ImageCreateInfo.extent.height = InHeight;
    ImageCreateInfo.extent.depth = 1;
    ImageCreateInfo.mipLevels = 1;
    ImageCreateInfo.arrayLayers = 1;
    ImageCreateInfo.format = InFormat;
    ImageCreateInfo.tiling = vk::ImageTiling::eLinear;
    ImageCreateInfo.initialLayout = vk::ImageLayout::ePreinitialized;
    ImageCreateInfo.usage =  vk::ImageUsageFlagBits::eSampled;
    ImageCreateInfo.sharingMode = vk::SharingMode::eExclusive;
    ImageCreateInfo.samples = vk::SampleCountFlagBits::e1; 
   
    SetImageParams(ImageCreateInfo);

}

void CVulkanImage::SetImageParams(const vk::ImageCreateInfo & InImageCreateInfo)
{
    if(Image)
    {
        if(InImageCreateInfo!=ImageCreateInfo)
        {
            CVulkanContext::Get()->GetDevice().destroyImage(Image);
            CVulkanContext::Get()->GetMemoryPools()->DeallocateBlock(MemoryBlock);

            Image=nullptr;
            MemoryBlock.Reset();
        }
        else
        {
            return;
        }
    }

    ImageCreateInfo=InImageCreateInfo;

    Image = CVulkanContext::Get()->GetDevice().createImage(ImageCreateInfo);

    BindMemory(Image);
}

void CVulkanImage::SetDepthImageParams(uint32_t InWidth, uint32_t InHeight)
{
    vk::ImageCreateInfo ImageCreateInfo;
    ImageCreateInfo.imageType = vk::ImageType::e2D;
    ImageCreateInfo.extent.width = InWidth;
    ImageCreateInfo.extent.height = InHeight;
    ImageCreateInfo.extent.depth = 1;
    ImageCreateInfo.mipLevels = 1;
    ImageCreateInfo.arrayLayers = 1;
    ImageCreateInfo.format = vk::Format::eD32Sfloat;
    ImageCreateInfo.tiling = vk::ImageTiling::eOptimal;
    ImageCreateInfo.initialLayout = vk::ImageLayout::eUndefined;
    ImageCreateInfo.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
    ImageCreateInfo.sharingMode = vk::SharingMode::eExclusive;
    ImageCreateInfo.samples = vk::SampleCountFlagBits::e1; 

    SetImageParams(ImageCreateInfo);
    
}

void CVulkanImage::WriteData(
    const uint8_t *InImagePixelData
    , uint64_t InPixelDataSize
    , uint32_t InPitch
    )
{

    vk::ImageSubresource ImageSubresource=vk::ImageSubresource()
        .setAspectMask(vk::ImageAspectFlagBits::eColor)
        .setMipLevel(0)
        .setArrayLayer(0);

    vk::SubresourceLayout SubresourceLayout=CVulkanContext::Get()->GetDevice()
        .getImageSubresourceLayout(Image, ImageSubresource);


    uint8_t* DataPtr =(uint8_t*) CVulkanContext::Get()->GetDevice().mapMemory(MemoryBlock.Memory, MemoryBlock.Offset, InPixelDataSize);
    if(InPitch== SubresourceLayout.rowPitch)
    {
        memcpy(DataPtr, InImagePixelData, InPixelDataSize);
    }
    else
    {
        int32_t MinPitch=CMathBase::Min((uint64_t)InPitch, SubresourceLayout.rowPitch);
        //copy data line by line
        for (uint32_t i=0; i < ImageCreateInfo.extent.height; i++)
        {
            memcpy(DataPtr + SubresourceLayout.rowPitch * i
                , InImagePixelData + InPitch * i
                ,MinPitch);
        }
    }
    CVulkanContext::Get()->GetDevice().unmapMemory(MemoryBlock.Memory);
}

void CVulkanImage::BindMemory(vk::Image InImage)
{
    vk::MemoryRequirements MemoryRequirements = CVulkanContext::Get()->GetDevice().getImageMemoryRequirements(Image);
    
    uint32_t MemoryTypeIndex=CVulkanContext::Get()->GetMemoryTypeIndex(MemoryRequirements, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
    
    MemoryBlock = CVulkanContext::Get()->GetMemoryPools()->AllocateBlock(
        EVulkanMemoryUsage::Texture
        ,MemoryTypeIndex
        ,MemoryRequirements
    );

    CVulkanContext::Get()->GetDevice().bindImageMemory(Image, MemoryBlock.Memory, MemoryBlock.Offset);
}
